package com.chang.annoAop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

// 切面类
@Component
@Aspect // 标识为一个切面类
public class LogAspect {

    // 设置切入点和通知类型
    // 通知类型: 前置通知 @Before(value = "切入点表达式配置切入点")
    // 切入点表达式：execution(访问修饰符 增强方法返回类型 增强方法所在类全路径,方法名称(方法参数))
    @Before(value = "execution(public * com.chang.annoAop.CalculatorImpl.count(..))")
    public void beforeMethod() {
        System.out.println("前置通知>>>方法执行前通知>>>>>>");
    }

    // 返回通知 @AfterReturning,
    @AfterReturning(value = "execution(public * com.chang.annoAop.CalculatorImpl.count(..))", returning = "result")
    public void AfterReturning(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        Object[] params = joinPoint.getArgs();
        String str = result.toString();
        System.out.println("Aop方法返回后通知>>>>>>方法名:" + methodName + "参数:" + Arrays.toString(params) + "目标方法最终返回结果：" + str);
    }

    // 异常通知 @AfterThrowing,
    @AfterThrowing(value = "com.chang.annoAop.LogAspect.pointCut()", throwing = "ex")
    public void AfterThrowing(JoinPoint joinPoint, Throwable ex) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("目标方法" + methodName + "异常通知,异常为：" + ex);
    }

    // 后置通知 @After(),
    @After(value = "pointCut()")  // 也可以使用已经声明相同表达式的方法来进行复用
    public void After() {
        System.out.println("后置通知>>>在目标方法最终结束后通知>>>>>>");
    }

    // 环绕通知 @Around() 环绕通知主要是用ProceedingJoinPoint来调用被代理的方法,从而控制在方法输出前后执行横向程序
    @Around(value = "execution(public int com.chang.annoAop.CalculatorImpl.count(..))")
    public Object Around(ProceedingJoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        String params = Arrays.toString(joinPoint.getArgs());
        Object result = null;
        try {
            System.out.println("环绕通知>>目标方法之前执行");

            // 调用目标方法
            result = joinPoint.proceed();
            System.out.println("环绕通知>>在目标方法返回后进行通知");

        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕通知>>目标方法出现异常时执行");
        } finally {
            System.out.println("环绕通知>>目标方法执行完毕之后通知");
        }
        return result;
    }

    @Pointcut(value = "execution(public int com.chang.annoAop.CalculatorImpl.count(..))")
    public void pointCut() {

    }

}
